<?php
namespace Api\Http;

use Api\Application;
use Api\Exception\NotImplemented;
use Api\Exception\NotImplementedException;
use Api\Renderer\Template;
use Api\Rest\Resource;
use Api\Utils\Arrays;
use Api\Utils\DOM;
use Api\Utils\Xml;
use Api\Xml\Schema;

/**
 * Class Response
 * @package Api\Http
 * @author Steffen Brem <steffenbrem@gmail.com>
 */
class Response
{
    const OK                            = 200;
    const CREATED                       = 201;
    const BAD_REQUEST                   = 400;
    const INTERNAL_SERVER_ERROR         = 500;

    /**
     * @var \Api\Application
     */
    protected $application;

    protected $code;

    /**
     * @var array
     */
    protected $data = array();

    /**
     * @param $code
     * @param array $data
     * @param Resource $resource
     */
    public function __construct($code, array $data = array(), Resource $resource = null)
    {
        $this->application = Application::$shared;

        $this->code = $code;
        $this->data = $data;

        if ($resource)
        {
            // Response can get trough some XSD validation
            if ($resource->getXmlSchema())
            {
                $schemaPath = $this->application->config['application']['path'] . '/xml/schemas';
                $templatePath = $this->application->config['application']['path'] . '/xml/templates';

                $root =& $this->data[$resource->getRootElement()];

                if (is_array($root))
                {
                    if (Arrays::isAssoc($root))
                    {
                        $root = array($root);
                    }

                    $file       = new Template\File($templatePath . '/' . $resource->getXmlTemplate(), $this->data);
                    $template   = new Template($file);

                    $xml = $template->render(true);

                    $schema = new Schema($schemaPath . '/' . $resource->getXmlSchema());
                    $dom = $schema->validate($xml);

                    // Convert XML template to array
                    $this->data = Xml::xmlToArray($dom);

                    // Fix single result
                    $request = $this->application->bootstrap->getRequest();

                    if ($request->getSingleResult() || $request->getIdentifier())
                    {
                        if (isset($this->data[$resource->getRootElement()]))
                        {
                            if (!isset($this->data[$resource->getRootElement()]['0']))
                            {
                                $this->data[$resource->getRootElement()] = array();
                            }
                            else
                            {
                                $this->data[$resource->getRootElement()] = $this->data[$resource->getRootElement()]['0'];
                            }
                        }
                        else
                        {
                            $this->data = $this->data['0'];
                        }
                    }
                }
            }

        }

    }

    /**
     * @return array
     */
    public function getData()
    {
        return $this->data;
    }

    /**
     * Render response output
     */
    public function output()
    {
        ob_end_clean();

        header('X-PHP-Response-Code: ' . $this->code, null, $this->code);

        $alt = $this->application->bootstrap->getRequest()->getAlt();

        switch ($alt)
        {
            case "json":
                echo $this->toJson();
                break;
            default:
                throw new NotImplementedException("Response format '" . $alt . "' is not implemented.");
        }

        die;
    }

    /**
     * Convert response to json
     */
    protected function toJson()
    {
        header("Content-Type: application/json; charset=utf-8");

        $json = ($this->application->config['application']['environment'] == Application::DEVELOPMENT)
            ? $this->prettyJson(json_encode($this->data))
            : json_encode($this->data);

        return $json;
    }

    /**
     * Pretty print JSON
     *
     * @param $json
     * @return string
     */
    protected function prettyJson($json)
    {
        $result      = '';
        $pos         = 0;
        $strLen      = strlen($json);
        $indentStr   = '  ';
        $newLine     = "\n";
        $prevChar    = '';
        $outOfQuotes = true;

        for ($i=0; $i<=$strLen; $i++) {

            // Grab the next character in the string.
            $char = substr($json, $i, 1);

            // Are we inside a quoted string?
            if ($char == '"' && $prevChar != '\\') {
                $outOfQuotes = !$outOfQuotes;

                // If this character is the end of an element,
                // output a new line and indent the next line.
            } else if(($char == '}' || $char == ']') && $outOfQuotes) {
                $result .= $newLine;
                $pos --;
                for ($j=0; $j<$pos; $j++) {
                    $result .= $indentStr;
                }
            }

            // Add the character to the result string.
            $result .= $char;

            // If the last character was the beginning of an element,
            // output a new line and indent the next line.
            if (($char == ',' || $char == '{' || $char == '[') && $outOfQuotes) {
                $result .= $newLine;
                if ($char == '{' || $char == '[') {
                    $pos ++;
                }

                for ($j = 0; $j < $pos; $j++) {
                    $result .= $indentStr;
                }
            }

            $prevChar = $char;
        }

        return $result;
    }
}